/***************************************************************************
 * Copyright : thierry lorthiois - octobre 2007
 ***************************************************************************/
#include "fx.h"
#include "FXDir.h"
#include "system.h"
#include "FXFTP.h"


struct MemoryStruct {
   char *memory;
   size_t size;
};

// store data in memory
int write1(void *buffer, size_t size, size_t nmemb, void *data)
{
   size_t realsize = size * nmemb;
   struct MemoryStruct *mem = (struct MemoryStruct *)data;

   // There might be a realloc() out there that doesn't like reallocing
   // NULL pointers, so we take care of it here
   if (mem->memory) mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
   else mem->memory = (char*)malloc(realsize + 1);

   if (mem->memory) {
      memcpy(&(mem->memory[mem->size]), buffer, realsize);
      mem->size += realsize;
      mem->memory[mem->size] = 0;
   }
   //printf("write1 %d\n", realsize);
   return realsize;
}


int write2(void *buffer, size_t size, size_t nmemb, void *data)
{
   size_t n;
   FXFTP *out=(FXFTP *)data;
   n = fwrite(buffer, size, nmemb, out->stream);
   out->tailleEnCours += n;

   if (out->tailleTotal==0) {
      CURLcode res;
      double ct1;
      res = curl_easy_getinfo(out->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &ct1);
      if(res == CURLE_OK)  out->tailleTotal = (long long)ct1;
   }
   //printf("write2 %d\n", n);
   return n;
}


int read2(void *buffer, size_t size, size_t nmemb, void *data)
{
   size_t n;
   FXFTP *out=(FXFTP *)data;

   n = fread(buffer, size, nmemb, out->stream) * size;
   out->tailleEnCours += n;
   //printf("upload %d\n", n);
   return n;
}


FXFTP::FXFTP(const FXString &src, const FXString &dest, const FXString &pattern) : FXThread()
{
   if ((src.right(1) == "/") || (dest.right(1) == "/")) {
      erreur = "FXFTP: la source ou la destination sont incorrectes";
      return;
   }

   tailleTotal = tailleEnCours = 0;
   stream = NULL;
   filePattern = pattern;
   if (src.left(5) == "ftp:/") {
      server = src;
      localDirectory = dest;
      mode = DOWNLOAD;
   }
   else {
      server = dest;
      localDirectory = src;
      mode = UPLOAD;
   }

   curl_global_init(CURL_GLOBAL_DEFAULT);
}


FXFTP::~FXFTP()
{
   curl_global_cleanup();
}


FXint FXFTP::run()
{
   if(!erreur.empty()) return 0;

   if (mode == DOWNLOAD) download();
   else upload();

   return 0;
}


void FXFTP::upload()
{
   CURLcode res;
   struct curl_slist *headerList=NULL;
   FXString *tabFile, command;
   int nbFile;

   // recherche des fichiers à transférer
   nbFile = FXDir::listFiles(tabFile, localDirectory, filePattern);
   
   curl = curl_easy_init();
   //curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE);
   curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
   curl_easy_setopt(curl, CURLOPT_READFUNCTION, read2);
   curl_easy_setopt(curl, CURLOPT_READDATA, this);
   FXString fileSrc, url;
   for (int i=0; i < nbFile ; i++) {
      fileSrc = localDirectory +"/"+ tabFile[i];
      url = server +"/"+ tabFile[i];

      stream = fopen(fileSrc.text(), "rb");
      if(!stream) {
         erreur = "FXFTP::upload: Impossible d'ouvrir " + fileSrc;
         break;
      }

      //printf("upload %s to %s\n", fileSrc.text(), url.text());
      curl_easy_setopt(curl, CURLOPT_URL, url.text());
      if (!postCommand.empty()) {
         command = postCommand +"/"+ tabFile[i];
         headerList = curl_slist_append(headerList, command.text());
         curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerList);
      }
      res = curl_easy_perform(curl);

      if (headerList != NULL) {
         curl_slist_free_all(headerList);
         headerList = NULL;
      }
      if(stream) fclose(stream);
      if(CURLE_OK != res) {
         erreur = "FXFTP::upload: " + FXStringVal(res);
         break;
      }
   }
   
   curl_easy_cleanup(curl);
   if (tabFile) delete[] tabFile;
}


void FXFTP::download()
{
   // créer le fichier de destination
   CURLcode res;
   struct curl_slist *headerList=NULL;
   FXString *tabFile, command;
   int nbFile;

   nbFile = listServerFiles(tabFile);
   // nbFile = 0; pour faire juste une consultation du site
   if (nbFile == 0) {
      if (tabFile) delete[] tabFile;
      return;
   }

   curl = curl_easy_init();
   //curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE);
   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write2);
   curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);   
   FXString fileDest, url;
   for (int i=0; i < nbFile ; i++) {
      url = server +"/"+ tabFile[i];
      fileDest = localDirectory +"/"+ tabFile[i];
      stream=fopen(fileDest.text(), "wb");
      if(!stream) {
         erreur = "FXFTP::download: Impossible d'ouvrir " + fileDest;
         break;
      }

      //printf("download %s to %s\n", url.text(), fileDest.text());
      curl_easy_setopt(curl, CURLOPT_URL, url.text());
      if (!postCommand.empty()) {
         command = postCommand +"/"+ tabFile[i];
         headerList = curl_slist_append(headerList, command.text());
         curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerList);
      }
      res = curl_easy_perform(curl);

      if (headerList != NULL) {
         curl_slist_free_all(headerList);
         headerList = NULL;
      }
      if(stream) fclose(stream);
      if(CURLE_OK != res) {
         erreur = "FXFTP::download: " + FXStringVal(res);
         FXFile::remove(fileDest);
         break;
      }
   }

   curl_easy_cleanup(curl);
   if (tabFile) delete[] tabFile;
}


int FXFTP::listServerFiles(FXString *&tabFile)
{
   CURLcode res;
   FXString url;
   struct MemoryStruct chunk;

   chunk.memory = NULL;
   chunk.size = 0;
   curl = curl_easy_init();
   //curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE);
   FXString str = "LIST "+filePattern;
   curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, str.text());
   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write1);
   curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
   
   url = server + "/";
   curl_easy_setopt(curl, CURLOPT_URL, url.text());
   res = curl_easy_perform(curl);

   if(CURLE_OK != res) {
      erreur = "FXFTP::listFilesFTP: " + FXStringVal(res);
      if (chunk.memory) free(chunk.memory);
      return 0;
   }
   curl_easy_cleanup(curl);
   
   //printf("liste des fichiers %d \n%s\n", chunk.size, chunk.memory);
   // compter les caracteres de fin de lignes
   int nbFile=0, pos=0;
   FXString ptr = chunk.memory;
   tabFile = NULL;
   while (pos >= 0) {
      pos = ptr.find('\n');
      if (pos >= 0) {
         nbFile++;
         ptr = ptr.mid(pos+1, ptr.length());
      }
   }

   // créer la liste des fichiers (hors '.' et '..')
   //printf("download %d\n", nbFile);
   if (nbFile > 0) {
      ptr = chunk.memory;
      FXString file, line;
      int id = 0;
      tabFile = new FXString[nbFile];
      for (int i=0 ; i < nbFile ; i++) {
         pos = ptr.find('\n');
         if (pos > 0) {
            // ligne sans le retour (windows/linux)
#ifdef WIN32
            line = ptr.left(pos-1);
#else
            line = ptr.left(pos);
#endif
            ptr = ptr.mid(pos+1, ptr.length());
            file = line.mid(line.rfind(' ')+1, line.length());
            printf("  fichier :%s:\n", file .text());
            if ((file != ".") && (file != "..")) {
               //printf("fichier %s\n", str.text());
               tabFile[id] = file;
               id++;
            }
         }
      }
      nbFile = id;
   }
   if (chunk.memory) free(chunk.memory);
   return nbFile;
}


